/*  File Name	: Logger.cpp
 *  Author		: Javie Pong
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-4-22
 *  Description:
 */

#include "Logger.h"
#include "CommonStruct.h"
#include "MsgBlockPool.h"
//#include "Task.h"
#include "Thread.h"
#include "Configurator.h"

bool Logger::is_stop_ = true;

Logger::Logger()
{
	this->log_list_ = new DuplexList(10240);
	this->msg_pool_ = new MsgBlockPool(1024,"Logger-Msg");
	this->log_dir_ 		= "./log";
	this->log_sub_dir_	= "";
	this->debug_pre_ 	= "Debug >>(%P|%p|%T)";
	this->info_pre_ 	= "Info >>>(%P|%p|%T)";
	this->error_pre_ 	= "Error >>(%P|%p|%T)";
}

Logger::~Logger()
{
	if(NULL!=this->file_info_.file)
	{
		::fclose(this->file_info_.file);
		this->file_info_.file = NULL;
	}
	if(NULL!=this->log_list_)
	{
		delete this->log_list_;
		this->log_list_ = NULL;
	}
}

void Logger::FileInfo::reset(void)
{
	file=NULL;
	write_time = 0;
	memset(&time,'\0',sizeof(struct tm));
	memset(dir,'\0',sizeof(dir));
}

Logger* Logger::instance_ =NULL;

RT_MUTEX Logger::rt_mutex_;

Logger *Logger::instance(void)
{
	if (NULL == instance_)
	{
		LockGuardReturn(RT_MUTEX,lock,rt_mutex_,NULL);
		if (NULL == instance_)
		{
			instance_ = new Logger();
		}
	}
	return instance_;
}

tm Logger::now_time(void)
{
	time_t tt = time(0);
	tm *t = ::localtime(&tt);
	return *t;
}

//get now time as format YYYY-MM-DD
std::string Logger::now_time_str(tm &t)
{
    char now[64]={0};
    time_t tt = mktime(&t);
    strftime(now, 64, "%Y-%m-%d", localtime(&tt));
    return now;
}

//get now time as format YYYY-MM-DD HH:mm:ss
std::string Logger::now_time_str_sec(void)
{
    char now[64]={0};
    time_t t = time(0);
    strftime(now, 64,"%Y-%m-%d %H:%M:%S", localtime(&t));
    return now;
}

int Logger::open(std::string log_sub_dir,std::string log_dir)
{
	if(!log_dir.empty())
	{
		this->log_dir_ = log_dir;
	}
	if(!log_sub_dir.empty())
	{
		this->log_sub_dir_ = log_sub_dir;
	}
	this->is_stop_ = false;
	Thread *log_thread = new Thread();
	log_thread->open(instance_,Logger::run_log_loop,true,"Logger");
	log_thread->activate();
	return 0;
}

void Logger::set_pre_format(LogType type,std::string pre_format)
{
	if(type==DEBUG_LOG)
	{
		this->debug_pre_ = pre_format;
	}
	else if( type == INFO_LOG)
	{
		this->info_pre_ = pre_format;
	}
	else if(type == ERROR_LOG)
	{
		this->error_pre_ = pre_format;
	}
}

int Logger::myvsprintf(char *buf, const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	int result = ::vsprintf(buf, format, ap);
	va_end(ap);
	return result;
}

int Logger::run_log_loop(void *p)
{
	Logger *logger = (Logger*) p;

	while (true)
	{
		MessageBlock *mb = NULL;
		if (logger->log_list_->get_obj((void*&) mb) == 0 && NULL != mb)
		{
#ifdef DEBUG
			int print = CONFIG_INSTANCE->daemon()["log_setting"]["console"].asInt();
			if (print == 1)
			{
				std::cout << mb->rd_ptr() << std::endl;
			}
#endif
			logger->write_log_to_file(mb);
			logger->msg_pool_->push(mb);
		}
		else if(is_stop_)
		{
#ifdef DEBUG
			int print = CONFIG_INSTANCE->daemon()["log_setting"]["console"].asInt();
			if (print == 1)
			{
				char log[256]={0};
				sprintf(log,"(%d|%ld|%s)Logger stop",getpid(),pthread_self(),now_time_str_sec().c_str());
				std::cout<<log<<std::endl;
			}
#endif
			if(NULL!=logger->file_info_.file)
			{
				::fflush(logger->file_info_.file);
				::fclose(logger->file_info_.file);
			}
			return 0;
		}
		else
		{
			usleep(1000);
		}
	}
	return 0;
}

int Logger::write_log_to_file(MessageBlock *mb)
{
	if(NULL==this->file_info_.file ||
			this->file_info_.time.tm_hour!=this->now_time().tm_hour)
	{
		FILE *old_file = this->file_info_.file;
		if(NULL!=old_file)
		{
			::fclose(old_file);
		}
		this->file_info_.reset();
		//::mkdir(this->log_dir_.c_str(), S_IRWXU);

		this->file_info_.time = this->now_time();
		sprintf(this->file_info_.dir,"%s/%s/%s",this->log_dir_.c_str(),
				this->now_time_str(this->file_info_.time).c_str(),this->log_sub_dir_.c_str());

		LOG((INFO_LOG,"Logger::open:[%s]",this->log_dir_.c_str()));
		std::string cmd="mkdir -p " + std::string(this->file_info_.dir);
		system(cmd.c_str());

		//::mkdir(this->file_info_.dir, S_IRWXU);

		sprintf(this->file_info_.dir+strlen(this->file_info_.dir),"/%02d.log",
				this->file_info_.time.tm_hour);

		this->file_info_.file = ::fopen(this->file_info_.dir,"a+");
		if(this->file_info_.file ==NULL)
		{
			return -1;
		}
	}

	::fwrite(mb->rd_ptr(),mb->data_len(),1,this->file_info_.file);
	::fwrite("\n",strlen("\n"),1,this->file_info_.file);

	//if(this->file_info_.write_time < time(0)-1)
	{
		::fflush(this->file_info_.file);
		this->file_info_.write_time = time(0);
	}
	return 0;
}

void Logger::stop_logger(void)
{
	this->is_stop_ = true;
}

int Logger::log(LogType type,const char *format,...)
{
	if(type==DEBUG_LOG)
	{
		int debug = CONFIG_INSTANCE->daemon()["log_setting"]["debug"].asInt();
		if(debug!=1)
		{
			return 0;
		}
	}

	std::string format_str="";
	if (DEBUG_LOG == type)
	{
		format_str = this->debug_pre_ + format;
	}
	else if (INFO_LOG == type)
	{
		format_str = this->info_pre_ + format;
	}
	else if (ERROR_LOG == type)
	{
		format_str = this->error_pre_ + format;
	}

	va_list argp;
	va_start(argp, format);

	MessageBlock *msg = this->parse_format(format_str.c_str(), argp);

	if(NULL!=msg)
	{
		this->log_list_->put_obj(msg);
	}
	va_end(argp);
	return 0;
}

MessageBlock* Logger::parse_format(const char *format_str,va_list argp)
{
	int length = strlen(format_str);

	char format[16]={0};
	char *ft = format;
	MessageBlock *msg = NULL;
	this->msg_pool_->pop(msg);
	if(NULL==msg)
	{
		return NULL;
	}

	char *insert = msg->wr_ptr();

	int total_len = length;

	while (*format_str != '\0' && length > 0)
	{
		if (*format_str != '%')
		{
			*(insert++)=*(format_str++);
			length--;
			continue;
		}
		else
		{
			if(format_str[1]=='%')
			{
				*insert++=*format_str ++;
				*insert++=*format_str ++;
				length--;
				continue;
			}
			memset(format,'0',sizeof(format));
			ft = format;
			*(ft++) = *(format_str++);//copy %

			int len = 0;
			switch(*format_str)
			{
				case 'P'://pid
				{
					strcpy(ft,"d");
					len = myvsprintf(insert,format,getpid());
					insert += len;
					break;
				}
				case 'p'://thread id
				{
					strcpy(ft, "lu");
					pthread_t thread_id = pthread_self();
					len = myvsprintf(insert,format, thread_id);
					insert += len;
					break;
				}
				case 't': //current timestamp
				{
					strcpy(ft, "ld");
					time_t tt = time(0);
					len = myvsprintf(insert, format, tt);
					insert += len;
					break;
				}
				case 'T'://current time:year-month-day-hour-minute-second
				{
					strcpy(ft,"s");
				    char now[64]={0};
				    time_t tt = time(0);
					strftime(now, 64, "%Y-%m-%d %H:%M:%S", localtime(&tt));
					snprintf(insert,1024,format,now);
					len = strlen(now);
					insert +=len;
					break;
				}
				case 'd'://int
				{
					strcpy(ft,"d");
					int var = va_arg(argp,int);
					len = myvsprintf(insert,format,var);
					insert += len;
					break;
				}
				case 'D'://double
				case 'f'://float
				{
					strcpy(ft, "f");
					double var = va_arg(argp,double);
					len = myvsprintf(insert, format, var);
					insert += len;
					break;
				}
				case 's'://string char*
				{
					strcpy(ft,"s");
					char *var = va_arg(argp,char*);
					snprintf(insert,1024,format,var);
					len = strlen(var);
					insert +=len;
					break;
				}
				case 'l'://long int
				{
					strcpy(ft,"ld");
					long var = va_arg(argp,long);
					len = myvsprintf(insert, format, var);
					insert +=len;
					break;
				}
				case 'L'://unsigned long int
				{
					strcpy(ft,"lu");
					unsigned long var = va_arg(argp,unsigned long);
					len = myvsprintf(insert, format, var);
					insert +=len;
					break;
				}
				case '@': //char*
				{
					strcpy(ft, "p");
					void *var = va_arg(argp,void*);
					len = myvsprintf(insert, format, var);
					insert += len;
					break;
				}
				default:
					break;
			}
			total_len += (len-2);
		}
		format_str++;
	}
	msg->wr_ptr(total_len);
	msg->wr_len(total_len);
	return msg;
}
